package com.hnu.store.utils;

import com.hnu.store.service.ex.FileNotFoundException;
import it.sauronsoftware.jave.Encoder;
import it.sauronsoftware.jave.MultimediaInfo;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.UnsupportedAudioFileException;
import lombok.extern.slf4j.Slf4j;
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException;
import org.jaudiotagger.audio.exceptions.ReadOnlyFileException;
import org.jaudiotagger.audio.mp3.MP3AudioHeader;
import org.jaudiotagger.audio.mp3.MP3File;
import org.jaudiotagger.tag.TagException;
import org.springframework.stereotype.Component;

@Component
@Slf4j
public class CutAudio {

  /**
   * 截取wav音频文件
   *
   * @param source     源文件地址
   * @param targetFile 目标文件地址
   * @param start      截取开始时间（秒）
   * @param end        截取结束时间（秒） return  截取成功返回true，否则返回false
   */
  public static boolean cut(String source, String targetFile, int start, int end) {
    try {
      //  文件类型不是wav
      if (!source.toLowerCase().endsWith(".wav") || !targetFile.toLowerCase().endsWith(".wav")) {
        return false;
      }
      //  获取源文件
      File wav = new File(source);
      if (!wav.exists()) {
        return false;
      }
      //  总时长(秒)
      long t1 = getTimeLen(wav);
      //  时长的逻辑错误
      if (start < 0 || end <= 0 || start >= t1 || end > t1 || start >= end) {
        return false;
      }
      FileInputStream fis = new FileInputStream(wav);
      //  音频数据大小（44为128kbps比特率wav文件头长度）
      long wavSize = wav.length() - 44;
      //  截取的音频数据大小
      long splitSize = (wavSize / t1) * (end - start);
      //  截取时跳过的音频数据大小
      long skipSize = (wavSize / t1) * start;
      int splitSizeInt = Integer.parseInt(String.valueOf(splitSize));
      int skipSizeInt = Integer.parseInt(String.valueOf(skipSize));
      //存放文件大小,4代表一个int占用字节数
      ByteBuffer buf1 = ByteBuffer.allocate(4);
      //放入文件长度信息
      buf1.putInt(splitSizeInt + 36);
      //代表文件长度
      byte[] flen = buf1.array();
      //存放音频数据大小，4代表一个int占用字节数
      ByteBuffer buf2 = ByteBuffer.allocate(4);
      //放入数据长度信息
      buf2.putInt(splitSizeInt);
      //代表数据长度
      byte[] dlen = buf2.array();
      //数组反转
      flen = reverse(flen);
      dlen = reverse(dlen);
      //定义wav头部信息数组
      byte[] head = new byte[44];
      //读取源wav文件头部信息
      fis.read(head, 0, head.length);
      /*
        4代表一个int占用字节数
        替换原头部信息里的文件长度
        替换原头部信息里的数据长度
       */
      for (int i = 0; i < 4; i++) {
        head[i + 4] = flen[i];
        head[i + 40] = dlen[i];
      }
      /*  存放截取的音频数据
         放入修改后的头部信息
       */
      byte[] fbyte = new byte[splitSizeInt + head.length];
      System.arraycopy(head, 0, fbyte, 0, head.length);
      /*
         存放截取时跳过的音频数据
         跳过不需要截取的数据
         读取要截取的数据到目标数组
       */
      byte[] skipBytes = new byte[skipSizeInt];
      fis.read(skipBytes, 0, skipBytes.length);
      fis.read(fbyte, head.length, fbyte.length - head.length);
      fis.close();

      File target = new File(targetFile);
      //如果目标文件已存在，则删除目标文件
      if (target.exists()) {
        target.delete();
      }
      FileOutputStream fos = new FileOutputStream(target);
      fos.write(fbyte);
      fos.flush();
      fos.close();
    } catch (IOException e) {
      e.printStackTrace();
      return false;
    }
    return true;
  }

  /**
   * 获取录音文件总时长（s）
   *
   * @param file 文件
   * @return 时长
   */
  public static long getTimeLen(File file) {
    long tlen = 0;
    if (file != null && file.exists()) {
      Encoder encoder = new Encoder();
      try {
        MultimediaInfo m = encoder.getInfo(file);
        tlen = m.getDuration();
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
    return tlen / 1000;
  }

  /**
   * 数组反转
   *
   * @param array 数组
   */
  public static byte[] reverse(byte[] array) {
    byte temp;
    int len = array.length;
    for (int i = 0; i < len / 2; i++) {
      temp = array[i];
      array[i] = array[len - 1 - i];
      array[len - 1 - i] = temp;
    }
    return array;
  }


  /**
   * 将文件转成字节流
   *
   * @param filePath mp3文件获取字节流
   * @return 返回字节流
   */
  public static byte[] getBytes(String filePath) {
    byte[] buffer = null;
    try {
      File file = new File(filePath);
      FileInputStream fis = new FileInputStream(file);
      ByteArrayOutputStream bos = new ByteArrayOutputStream(1000);
      byte[] b = new byte[1000];
      int n;
      while ((n = fis.read(b)) != -1) {
        bos.write(b, 0, n);
      }
      fis.close();
      bos.close();
      buffer = bos.toByteArray();
    } catch (FileNotFoundException | IOException e) {
      e.printStackTrace();
    }
    return buffer;
  }

//接下来是转换的代码。这里的切割方法只能针对wav格式，但是现在有很多音频文件是mp3个格式的；


  /**
   * mp3的字节数组生成wav文件
   *
   * @param sourceBytes mp3字节流
   * @param targetPath  目标路径
   */
  public static boolean byteToWav(byte[] sourceBytes, String targetPath) {
    if (sourceBytes == null || sourceBytes.length == 0) {
      System.out.println("Illegal Argument passed to this method");
      return false;
    }

    try (final ByteArrayInputStream bais = new ByteArrayInputStream(
        sourceBytes); final AudioInputStream sourceAIS = AudioSystem.getAudioInputStream(bais)) {
      AudioFormat sourceFormat = sourceAIS.getFormat();
      // 设置MP3的语音格式,并设置16bit
      AudioFormat mp3tFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
          sourceFormat.getSampleRate(), 16, sourceFormat.getChannels(),
          sourceFormat.getChannels() * 2, sourceFormat.getSampleRate(), false);
      // 设置百度语音识别的音频格式
      AudioFormat pcmFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 16000, 16, 1, 2,
          16000, false);
      try (
          // 先通过MP3转一次，使音频流能的格式完整
          final AudioInputStream mp3AIS = AudioSystem.getAudioInputStream(mp3tFormat, sourceAIS);
          // 转成百度需要的流
          final AudioInputStream pcmAIS = AudioSystem.getAudioInputStream(pcmFormat, mp3AIS)) {
        // 根据路径生成wav文件
        AudioSystem.write(pcmAIS, AudioFileFormat.Type.WAVE, new File(targetPath));
      }
      return true;
    } catch (IOException | UnsupportedAudioFileException e) {
      e.printStackTrace();
      System.out.println("文件转换异常：" + e.getMessage());
      return false;
    }
  }

  /**
   * @param beginTime 预备开始截取的时间ms
   * @param endTime   截取结算时间ms
   * @throws TagException               报错
   * @throws InvalidAudioFrameException 报错
   * @throws ReadOnlyFileException      报错
   * @throws IOException                报错
   */
  public static boolean mp3Cut(String source, String target, long beginTime, long endTime) {
    //  原mp3文件
    File mSourceMp3File = new File(source);
    if (!mSourceMp3File.exists()) {
      return false;
    }
    MP3File mp3 = null;
    try {
      mp3 = new MP3File(mSourceMp3File);
    } catch (IOException e) {
      e.printStackTrace();
      throw new RuntimeException(e);
    } catch (TagException e) {
      e.printStackTrace();
      throw new RuntimeException(e);
    } catch (ReadOnlyFileException e) {
      e.printStackTrace();
      throw new RuntimeException(e);
    } catch (InvalidAudioFrameException e) {
      e.printStackTrace();
      throw new RuntimeException(e);
    }
    MP3AudioHeader header = (MP3AudioHeader) mp3.getAudioHeader();
    long bitRateKbps = header.getBitRateAsNumber();
    //  1KByte/s=8Kbps, bitRate *1024L / 8L / 1000L 转换为 bps 每毫秒
    //  计算出开始字节位置
    long beginBitRateBpm = (bitRateKbps * 1024L / 8L / 1000L) * beginTime;
    //  返回音乐数据的第一个字节
    long firstFrameByte = header.getMp3StartByte();
    //  获取开始时间所在文件的字节位置
    long beginByte = firstFrameByte + beginBitRateBpm;
    //  计算出结束字节位置
    long endByte = beginByte + (bitRateKbps * 1024L / 8L / 1000L) * (endTime - beginTime);
    //  目标文件
    File dFile = new File(target);
    RandomAccessFile dRaf = null;
    try {
      dRaf = new RandomAccessFile(dFile, "rw");
    } catch (java.io.FileNotFoundException e) {
      e.printStackTrace();
      throw new RuntimeException(e);
    }
    RandomAccessFile sRaf = null;
    try {
      sRaf = new RandomAccessFile(mSourceMp3File, "rw");
    } catch (java.io.FileNotFoundException e) {
      e.printStackTrace();
      throw new RuntimeException(e);
    }
    //  先将mp3的头文件写入文件
    for (long i = 0; i < firstFrameByte; i++) {
      int m = 0;
      try {
        m = sRaf.read();
      } catch (IOException e) {
        e.printStackTrace();
        throw new RuntimeException(e);
      }
      try {
        dRaf.write(m);
      } catch (IOException e) {
        e.printStackTrace();
        throw new RuntimeException(e);
      }
    }
    System.out.println(endByte - beginByte);
    //  跳转到指定的位置
    try {
      sRaf.seek(beginByte);
    } catch (IOException e) {
      e.printStackTrace();
      throw new RuntimeException(e);
    }
    //  开始写入 mp3实体
    int m = -1;
    for (long i = 0; i <= endByte - beginByte; i++) {
      try {
        if ((m = sRaf.read()) != -1) {
          dRaf.write(m);
        }
      } catch (IOException e) {
        e.printStackTrace();
        throw new RuntimeException(e);
      }
    }
    try {
      sRaf.close();
    } catch (IOException e) {
      e.printStackTrace();
      throw new RuntimeException(e);
    }
    try {
      dRaf.close();
    } catch (IOException e) {
      e.printStackTrace();
      throw new RuntimeException(e);
    }
    return true;
  }

}



